﻿using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.IO;
using System.Net.Http;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using DoNet.Common;
using System.Net.Http.Headers;

namespace DoNet.Util
{
    /// <summary>
    /// 发起HTTP请求
    /// </summary>
    public class HttpUtil
    {
        /// <summary>
        /// 配置并返回一个 HttpClient 实例
        /// </summary>
        /// <param name="customHeaders">自定义请求头</param>
        /// <param name="proxySetting">代理设置</param>
        /// <param name="url">请求的 URL</param>
        /// <returns>配置好的 HttpClient 实例</returns>
        public HttpClient HttpClientSettings(string url, SortedDictionary<string, object> headers, string accept = "application/json", ProxySetting proxySetting = null)
        {
            // 创建 HttpClientHandler 并禁用自动解压缩
            var handler = new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.None };

            if (proxySetting != null && proxySetting.UseProxy)
            {
                // 在日志里记录是否使用代理，方便排查问题
                //Logger.CONNECT.InfoFormat("UseProxy is [{0}]", proxySetting.UseProxy);

                // 构建代理 URL
                string proxyUrl = string.Format("{0}://{1}:{2}", proxySetting.Protocol, proxySetting.Address, proxySetting.Port);
                //Logger.CONNECT.InfoFormat("Proxy url is [{0}]", proxyUrl);

                // 创建 Web 代理对象
                IWebProxy webProxy = new WebProxy(proxyUrl);
                if (proxySetting.ProxyCredentials != null &&
                    !string.IsNullOrEmpty(proxySetting.ProxyCredentials.UserName) &&
                    !string.IsNullOrEmpty(proxySetting.ProxyCredentials.Password))
                {
                    //Logger.CONNECT.InfoFormat("ProxyCredentials.UserName is [{0}]", proxySetting.ProxyCredentials.UserName);

                    // 设置代码身份信息
                    webProxy.Credentials = new NetworkCredential(proxySetting.ProxyCredentials.UserName, proxySetting.ProxyCredentials.Password);
                }
                handler.Proxy = webProxy; // 设置 HttpClientHandler 的代理
                handler.UseProxy = true; // 启用代理
            }

            // 创建 HttpClient 实例
            HttpClient httpclient = new HttpClient(handler);

            // 清除默认的 Accept 头
            httpclient.DefaultRequestHeaders.Accept.Clear();

            // 添加 Accept 头，指定接受 JSON 格式
            httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(accept));

            // 添加自定义请求头
            if (headers != null)
            {
                //var headerInfo = JsonConvert.SerializeObject(customHeaders);
                //Logger.CONNECT.InfoFormat("Custom header info: [{0}]", headerInfo);
                foreach (KeyValuePair<string, object> headerItem in headers)
                {
                    if (!string.IsNullOrEmpty(headerItem.Value.ToString()))
                    {
                        httpclient.DefaultRequestHeaders.Add(headerItem.Key, headerItem.Value.ToString().Trim());
                    }
                }
            }

            // 获取请求的 HTTP 模式（HTTP 或 HTTPS）
            HttpMode httpMode = getHttpModeByUrl(url);
            if (httpMode == HttpMode.HTTPS)
            {
                trustCertificate(); //如果是 HTTPS，忽略证书警告
            }

            // 返回配置好的 HttpClient 实例
            return httpclient;
        }

        /// <summary>
        /// 默认信任证书，忽略证书警告。
        /// </summary>
        private void trustCertificate()
        {
            //默认忽略证书
            ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
            //兼容所有ssl协议
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls | SecurityProtocolType.Ssl3;
        }

        /// <summary>
        /// 根据 URL 判断 HTTP 模式（HTTP 或 HTTPS）。
        /// </summary>
        /// <param name="url">请求的 URL</param>
        /// <returns>HTTP 模式</returns>
        private HttpMode getHttpModeByUrl(string url)
        {
            HttpMode httpMode = HttpMode.HTTP;
            if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
            {
                httpMode = HttpMode.HTTPS;
            }
            return httpMode;
        }

        /// <summary>
        /// 同步方式发送 HTTP 请求并返回响应字符串。
        /// </summary>
        /// <param name="url">请求的 URL</param>
        /// <param name="httpMethodType">HTTP 方法类型</param>
        /// <param name="paramlist">请求参数</param>
        /// <param name="customHeaders">自定义请求头</param>
        /// <param name="accept">接受数据格式，默认JSON</param>
        /// <param name="encoding">编码方式</param>
        /// <param name="proxySetting">代理设置</param>
        /// <returns>响应字符串</returns>
        public string HttpclientCall(string url, HttpMethodType httpMethodType, HttpContent paramlist, SortedDictionary<string, object> headers, string accept = "application/json", Encoding encoding = null, ProxySetting proxySetting = null)
        {
            HttpResponseMessage response = null;
            string result = string.Empty;
            try
            {
                // 配置 HttpClient 实例
                using (HttpClient httpclient = HttpClientSettings(url, headers, accept, proxySetting))
                {
                    CompressedContent content = null;
                    if (paramlist != null)
                    {
                        // 压缩请求内容
                        content = new CompressedContent(paramlist, CompressionType.GZip);
                    }

                    //Logger.CONNECT.InfoFormat("Call Url=[{0}], Method=[{1}]", url, httpMethodType.ToString());

                    switch (httpMethodType)
                    {
                        case HttpMethodType.GET:
                            response = httpclient.GetAsync(new Uri(url)).Result;
                            break;
                        case HttpMethodType.POST:
                            response = httpclient.PostAsync(new Uri(url), content).Result;
                            break;
                        case HttpMethodType.PUT:
                            response = httpclient.PutAsync(new Uri(url), content).Result;
                            break;
                        case HttpMethodType.DELETE:
                            response = httpclient.DeleteAsync(new Uri(url)).Result;
                            break;
                        default:
                            throw new NotSupportedException("This type is not supported!");
                    }

                    // 确保响应状态码为成功
                    response.EnsureSuccessStatusCode();

                    // 获取响应流
                    using (Stream responseStream = response.Content.ReadAsStreamAsync().Result)
                    {
                        if (encoding == null)
                        {
                            encoding = Encoding.UTF8;
                        }
                        // 创建 StreamReader 对象
                        using (StreamReader sr = new StreamReader(responseStream, encoding))
                        {
                            // 读取响应内容
                            result = sr.ReadToEnd();
                        }

                    }
                }
            }
            catch(Exception ex)
            {
                throw; // 重新抛出异常
            }
            finally
            {
                // 依次释放资源
                if (response != null) response.Dispose();
            }
            // 返回响应字符串
            return result;
        }

        /// <summary>
        /// 异步发送 HTTP 请求并返回响应字符串。
        /// </summary>
        /// <param name="url">请求的 URL</param>
        /// <param name="httpMethodType">HTTP 方法类型</param>
        /// <param name="paramlist">请求参数</param>
        /// <param name="customHeaders">自定义请求头</param>
        /// <param name="accept">接受数据格式，默认JSON</param>
        /// <param name="encoding">编码方式</param>
        /// <param name="proxySetting">代理设置</param>
        /// <returns></returns>
        public async Task<string> HttpclientCallAsync(string url, HttpMethodType httpMethodType, HttpContent paramlist, SortedDictionary<string, object> headers, string accept = "application/json", Encoding encoding = null, ProxySetting proxySetting = null)
        {
            HttpResponseMessage response = null;
            string result = string.Empty;
            try
            {
                using (HttpClient httpclient = HttpClientSettings(url, headers, accept, proxySetting))
                {
                    CompressedContent content = null;
                    if (paramlist != null)
                    {
                        content = new CompressedContent(paramlist, CompressionType.GZip);
                    }

                    //Logger.CONNECT.InfoFormat("Call Url=[{0}], Method=[{1}]", url, httpMethodType.ToString());

                    switch (httpMethodType)
                    {
                        case HttpMethodType.GET:
                            response = await httpclient.GetAsync(new Uri(url));
                            break;
                        case HttpMethodType.POST:
                            response = await httpclient.PostAsync(new Uri(url), content);
                            break;
                        case HttpMethodType.PUT:
                            response = await httpclient.PutAsync(new Uri(url), content);
                            break;
                        case HttpMethodType.DELETE:
                            response = await httpclient.DeleteAsync(new Uri(url));
                            break;
                        default:
                            throw new NotSupportedException("This type is not supported!");
                    }

                    response.EnsureSuccessStatusCode();

                    using (Stream responseStream = await response.Content.ReadAsStreamAsync())
                    {
                        if (encoding == null)
                        {
                            encoding = Encoding.UTF8;
                        }
                        using (StreamReader sr = new StreamReader(responseStream, encoding))
                        {
                            result = sr.ReadToEnd();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw;
            }
            finally
            {
                if (response != null) response.Dispose();
            }
            return result;
        }
    }
    /// <summary>
    /// 压缩内容
    /// </summary>
    public class CompressedContent : HttpContent
    {
        private readonly HttpContent _originalContent; // 存储原始的 HttpContent 对象
        private readonly CompressionType _compressionMethod; // 存储压缩方法

        /// <summary>
        /// 构造函数，初始化 CompressedContent 对象
        /// </summary>
        /// <param name="content">原始的 HttpContent 对象</param>
        /// <param name="compressionMethod">压缩方法（GZip 或 Deflate）</param>
        public CompressedContent(HttpContent content, CompressionType compressionMethod)
        {
            if (content == null)
            {
                throw new ArgumentNullException("content");
            }

            // 初始化原始内容
            _originalContent = content;

            // 初始化压缩方法
            _compressionMethod = compressionMethod;

            // 将原始内容的所有头部信息复制到新的 CompressedContent 对象中
            foreach (KeyValuePair<string, IEnumerable<string>> header in _originalContent.Headers)
            {
                Headers.TryAddWithoutValidation(header.Key, header.Value);
            }

            // 添加 Content-Encoding 头部，说明所使用的压缩方法
            Headers.ContentEncoding.Add(_compressionMethod.ToString().ToLowerInvariant());
        }

        /// <summary>
        /// 重写基类的TryComputeLength方法，直接设置长度为-1，返回false
        /// </summary>
        protected override bool TryComputeLength(out long length)
        {
            length = -1;
            return false;
        }

        /// <summary>
        /// 重写基类的SerializeToStreamAsync方法，异步序列化内容到指定的流中
        /// </summary>
        /// <param name="stream">目标流</param>
        /// <param name="context">传输上下文</param>
        /// <returns>任务对象</returns>
        protected async override Task SerializeToStreamAsync(Stream stream, TransportContext context)
        {
            if (_compressionMethod == CompressionType.GZip)
            {
                using (var gzipStream = new GZipStream(stream, CompressionMode.Compress, leaveOpen: true))
                {
                    await _originalContent.CopyToAsync(gzipStream);
                }
            }
            else if (_compressionMethod == CompressionType.Deflate)
            {
                using (var deflateStream = new DeflateStream(stream, CompressionMode.Compress, leaveOpen: true))
                {
                    await _originalContent.CopyToAsync(deflateStream);
                }
            }
        }
    }
}
